package com.gitee.wsl.net.ssdp.description.xml

object XmlParser {

    fun parse(xml: String): XmlElement {
        val xmlStart = if(xml.startsWith("<?xml")) xml.indexOf("?>") + 2 else 0

        return try {
            parseElement(xml.substring(xmlStart)).first
        } catch (e: Exception) {
            throw InvalidXmlException(e.message)
        }
    }

    private fun parseElement(xml: String): Pair<XmlElement, String> {
        var remaining = xml.trim()

        // Parse opening tag and attributes
        val openTagStart = remaining.indexOf('<') + 1
        val openTagEnd = remaining.indexOf('>')
        val tagContent = remaining.substring(openTagStart, openTagEnd).trim()
        val parts = tagContent.split(" ", limit = 2)
        val tagName = parts[0]
        if(!remaining.contains("</$tagName>")) {
            throw IllegalArgumentException("missing closing tag for $tagName")
        }
        val result = XmlElement(tagName)

        val attributes = mutableMapOf<String, String>()
        if (parts.size > 1) {
            val attrRegex = """([\w:]+)="([^"]*)"""".toRegex()
            attrRegex.findAll(parts[1]).forEach { match ->
                attributes[match.groupValues[1]] = match.groupValues[2]
            }
        }
        if (attributes.isNotEmpty()) {
            result.attributes = attributes
        }

        remaining = remaining.substring(openTagEnd + 1).trim()

        // Parse content inside the tag
        val children = mutableListOf<XmlElement>()
        var value: String? = null
        while (!remaining.startsWith("</$tagName>")) {
            if (remaining.startsWith('<') && !remaining.startsWith("</")) {
                // Parse nested element
                val (child, rest) = parseElement(remaining)
                children.add(child)
                remaining = rest.trim()
            } else {
                // Parse text content
                val textEnd = remaining.indexOf('<')
                val textContent = remaining.substring(0, textEnd).trim()
                if (textContent.isNotEmpty()) {
                    value = textContent
                }
                remaining = remaining.substring(textEnd).trim()
            }
        }

        // Assign children or value
        if (children.isNotEmpty()) {
            result.children = children
        } else if (value != null) {
            result.value = value
        }

        // Remove closing tag
        remaining = remaining.substring(remaining.indexOf(">") + 1).trim()

        return result to remaining
    }

    fun String.parseAsXml(): XmlElement = parse(this)
}